
#include <cstdio>
#include <cstring>

#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
using boost::system::error_code;
using boost::system::system_category;
using boost::system::system_error;

#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::posix_time;

#include "bocat/common/ini.hpp"
#include "bocat/common/logging.hpp"

#include "aubin.hpp"


namespace	fekcab	{


AubinConfig::AubinConfig( const char* ini_filename, const char* ini_section )
{
    bocat::common::INIFile  ini( ini_filename );

    std::string SECTION( ini_section );

	//// index 
    std::string index_skey_size = ini[ SECTION ][ "index.skey.size" ];
    if ( index_skey_size.size() < 1 ) {
		this->index_skey_size = 12;
    }
	else {
		this->index_skey_size = boost::lexical_cast< int32_t >( index_skey_size.c_str() );
		if ( this->index_skey_size < 8 ) {
			this->index_skey_size = 8;
		}
	}


	index_snap_home = ini[ SECTION ][ "index.snap.home" ];
    
	std::string	index_snap_copies = ini[ SECTION ][ "index.snap.copies" ];
	if ( index_snap_copies.size() > 0 ) {
		this->index_snap_copies = boost::lexical_cast< int32_t >( index_snap_copies.c_str() );
		if ( this->index_snap_copies < 1 ) {
			this->index_snap_copies = 5;
		}
	}
	else {
		this->index_snap_copies = 5;
	}
    
	std::string	index_snap_interval = ini[ SECTION ][ "index.snap.interval" ];
	if ( index_snap_interval.size() > 0 ) {
		this->index_snap_interval = boost::lexical_cast< int32_t >( index_snap_interval.c_str() );
		if ( this->index_snap_interval < 1 ) {
			this->index_snap_interval = 7200;
		}
	}
	else {
		this->index_snap_interval = 7200;
	}

	this->index_snap_key_encode_base64_b = false;
    std::string index_snap_key_encode = ini[ SECTION ][ "index.snap.key.encode" ];
    if ( index_snap_key_encode.size() > 0 ) {
		this->index_snap_key_encode = index_snap_key_encode;
		if ( 0 == strcmp(index_snap_key_encode.c_str(), "base64") ) {
			this->index_snap_key_encode_base64_b = true;
		}
	}

	this->index_snap_value_encode_base64_b = false;
    std::string index_snap_value_encode = ini[ SECTION ][ "index.snap.value.encode" ];
    if ( index_snap_value_encode.size() > 0 ) {
		this->index_snap_value_encode = index_snap_value_encode;
		if ( 0 == strcmp(index_snap_value_encode.c_str(), "base64") ) {
			this->index_snap_value_encode_base64_b = true;
		}
	}

	//// data
	data_home = ini[ SECTION ][ "data.home" ];
	if ( data_home.size() < 1 ) {
		std::ostringstream  msg;
		msg << "No 'data.home' configured in [" << SECTION << "] in " << ini_filename;

		throw std::runtime_error( msg.str() );
	}
    
    std::string data_file_slim = ini[ SECTION ][ "data.file.slim" ];
    if ( data_file_slim.size() < 1 ) {
		this->data_file_slim = 0;
    }
	else {
		this->data_file_slim = boost::lexical_cast< int32_t >( data_file_slim.c_str() );
	}

    std::string data_file_rotate_size = ini[ SECTION ][ "data.file.rotatesize" ];
    if ( data_file_rotate_size.size() < 1 ) {
		this->data_file_rotate_size = 0;
    }
	else {
		this->data_file_rotate_size = boost::lexical_cast< u_int32_t >( data_file_rotate_size.c_str() );
	}

	this->data_key_encode_base64_b = false;
    std::string data_key_encode = ini[ SECTION ][ "data.key.encode" ];
    if ( data_key_encode.size() > 0 ) {
		this->data_key_encode = data_key_encode;
		if ( 0 == strcmp(data_key_encode.c_str(), "base64") ) {
			this->data_key_encode_base64_b = true;
		}
	}

	this->data_value_encode_base64_b = false;
    std::string data_value_encode = ini[ SECTION ][ "data.value.encode" ];
    if ( data_value_encode.size() > 0 ) {
		this->data_value_encode = data_value_encode;
		if ( 0 == strcmp(data_value_encode.c_str(), "base64") ) {
			this->data_value_encode_base64_b = true;
		}
	}

	//// oplog
	oplog_home = ini[ SECTION ][ "oplog.home" ];

    std::string oplog_file_slim = ini[ SECTION ][ "oplog.file.slim" ];
    if ( oplog_file_slim.size() < 1 ) {
		this->oplog_file_slim = 0;
    }
	else {
		this->oplog_file_slim = boost::lexical_cast< int32_t >( oplog_file_slim.c_str() );
	}

    std::string oplog_file_rotate_size = ini[ SECTION ][ "oplog.file.rotatesize" ];
    if ( oplog_file_rotate_size.size() < 1 ) {
		this->oplog_file_rotate_size = 0;
    }
	else {
		this->oplog_file_rotate_size = boost::lexical_cast< u_int32_t >( oplog_file_rotate_size.c_str() );
	}

	std::string	oplog_file_tankage = ini[ SECTION ][ "oplog.file.tankage" ];
	if ( oplog_file_tankage.size() > 0 ) {
		this->oplog_file_tankage = boost::lexical_cast< int32_t >( oplog_file_tankage.c_str() );
		if ( this->oplog_file_tankage < 1 ) {
			this->oplog_file_tankage = 64 * 1024 * 1024;
		}
	}
	else {
		this->oplog_file_tankage = 64 * 1024 * 1024;
	}

	this->oplog_key_encode_base64_b = false;
    std::string oplog_key_encode = ini[ SECTION ][ "oplog.key.encode" ];
    if ( oplog_key_encode.size() > 0 ) {
		this->oplog_key_encode = oplog_key_encode;
		if ( 0 == strcmp(oplog_key_encode.c_str(), "base64") ) {
			this->oplog_key_encode_base64_b = true;
		}
	}

	this->oplog_value_encode_base64_b = false;
    std::string oplog_value_encode = ini[ SECTION ][ "oplog.value.encode" ];
    if ( oplog_value_encode.size() > 0 ) {
		this->oplog_value_encode = oplog_value_encode;
		if ( 0 == strcmp(oplog_value_encode.c_str(), "base64") ) {
			this->oplog_value_encode_base64_b = true;
		}
	}

}


///////////////////////////////////////////////////////////////////////
//


#define	STAT_OP_INTERVAL	50000

AubinMeter::AubinMeter( const std::string& id )
	:	_id( id )
{
	sum_ms_put_op = 0;		count_put_op = 0;
	sum_ms_get_op = 0;		count_get_op = 0;
	sum_ms_erase_op = 0;	count_erase_op = 0;

}

void
AubinMeter::countInsert( long ms )
{
	sum_ms_put_op += ms;
	++count_put_op;
	if ( count_put_op >= STAT_OP_INTERVAL ) {
		float	time_s = sum_ms_put_op / 1000.0f;
		float	tps = count_put_op / time_s;

		bocat::common::Logger*  logger_p = bocat::common::getLogger();
		logger_p->info(__LINE__,__FILE__, "   - PUT %s tps=%.2f", _id.c_str(), tps);

		count_put_op = 0;
		sum_ms_put_op = 0;
	}
}

void
AubinMeter::countFind( long ms )
{
	sum_ms_get_op += ms;
	++count_get_op;
	if ( count_get_op >= STAT_OP_INTERVAL ) {
		float	time_s = sum_ms_get_op / 1000.0f;
		float	tps = count_get_op / time_s;

		bocat::common::Logger*  logger_p = bocat::common::getLogger();
		logger_p->info(__LINE__,__FILE__, "   - GET %s tps=%.2f", _id.c_str(), tps);

		count_get_op = 0;
		sum_ms_get_op = 0;
	}
}

void
AubinMeter::countErase( long ms )
{
	sum_ms_erase_op += ms;
	++count_erase_op;
	if ( count_erase_op >= STAT_OP_INTERVAL ) {
		float	time_s = sum_ms_erase_op / 1000.0f;
		float	tps = count_erase_op / time_s;

		bocat::common::Logger*  logger_p = bocat::common::getLogger();
		logger_p->info(__LINE__,__FILE__, "   - ERASE %s tps=%.2f", _id.c_str(), tps);

		count_erase_op = 0;
		sum_ms_erase_op = 0;
	}
}


///////////////////////////////////////////////////////////////////////
//


int
checkSnapshotCopies( const char* snap_home, const char* filename_prefix, int32_t copies )
{

	bocat::common::Logger*  logger_p = bocat::common::getLogger();
	logger_p->info(__LINE__,__FILE__, "   - check snapshot copies (snap-home:%s fielname-prefix:%s copies:%d)", snap_home, filename_prefix, copies );

	if ( copies < 1 ) {
		return	0;
	}

	boost::filesystem::path	data_path( snap_home );

	if ( !boost::filesystem::exists( data_path ) ) {
		return	0;
	}

	int32_t	prefix_length = strlen( filename_prefix );

	while ( true ) {
		int	count_files = 0;
		std::time_t	oldest_mtime = time(NULL) + 86400;
		boost::filesystem::path	oldest_file;

		boost::filesystem::directory_iterator end_itr; 
		for ( boost::filesystem::directory_iterator itr( data_path );
			itr != end_itr;
			++itr ) {
			if (boost::filesystem::is_regular_file( itr->status() ) ) {
				std::string	fn = itr->path().filename();
				if ( 0 == strncmp( filename_prefix, fn.c_str(), prefix_length ) ) { 
					++count_files;

					time_t	mtime = boost::filesystem::last_write_time( itr->path() );
					if (mtime < oldest_mtime) {
						oldest_mtime = mtime;
						oldest_file = itr->path();
					}
				}
			}
		}

		if (count_files <= copies) {
			break;
		}

		std::string	file_to_delete = oldest_file.string();
		::remove( file_to_delete.c_str() );
	}

	return	0;
}

long
getTimeDuration( ptime time_begin, ptime time_end )
{
    time_period tp( time_begin, time_end );
    time_duration   td = tp.length();
    long    time_last_ms = td.total_milliseconds();

    return time_last_ms;
}


}	//// namespace fekcab


